#include "xlcomm/net/event_loop_thread.h"

#include "xlcomm/base/logger.h"

namespace xlcomm {
namespace net {

EventLoopThread::EventLoopThread(const std::string& name,
                                 const ThreadInitCallback& cb)
    : init_callback_(cb),
      event_loop_(nullptr),
      started_(false),
      thread_(std::bind(&EventLoopThread::ThreadProc, this)),
      name_(name) {
  LOG_INFO << "EventLoopThread '" << name_ << "' start...";
}

EventLoopThread::~EventLoopThread() {
  // Not 100% race-free, eg. ThreadProc could be running init_callback_.
  // Still a tiny chance to call destructed object, if ThreadProc exits just
  // now. But when EventLoopThread destructs, usually programming is exiting
  // anyway.
  if (event_loop_ != nullptr) {
    event_loop_->Quit();
    thread_.join();
  }
  LOG_INFO << "EventLoopThread '" << name_ << "' is stopped.";
}

EventLoop* EventLoopThread::StartLoop() {
  std::unique_lock<std::mutex> lock(mutex_);
  XLCOMM_ASSERT(!started_);
  while (event_loop_ == nullptr) {
    cond_.wait(lock);
  }
  started_ = true;
  return event_loop_;
}

void EventLoopThread::ThreadProc() {
  EventLoop loop;
  if (init_callback_) init_callback_(&loop);

  {
    std::lock_guard<std::mutex> lock(mutex_);
    event_loop_ = &loop;
    cond_.notify_one();
  }
  loop.Loop();

  std::lock_guard<std::mutex> lock(mutex_);
  // 线程退出后这个EventLoop指针就失效了，最好不要在外部调Quit
  event_loop_ = nullptr;
}

}  // namespace net
}  // namespace xlcomm